using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NodeCanvas.Variables;
using UnityEngine;

namespace NodeCanvas
{
	public abstract class Node : MonoBehaviour
	{
		[SerializeField]
		private List<Connection> _inConnections = new List<Connection>();

		[SerializeField]
		private List<Connection> _outConnections = new List<Connection>();

		[SerializeField]
		private Graph _graph;

		[SerializeField]
		private string _customName;

		[SerializeField]
		private string _tagName;

		private Status _status = Status.Resting;

		private string _nodeName;

		private string customName
		{
			get
			{
				return _customName;
			}
			set
			{
				_customName = value;
			}
		}

		public string tagName
		{
			get
			{
				return _tagName;
			}
			private set
			{
				_tagName = value;
			}
		}

		public new string name
		{
			get
			{
				return nodeName;
			}
		}

		public new string tag
		{
			get
			{
				return tagName;
			}
		}

		public virtual string nodeName
		{
			get
			{
				if (!string.IsNullOrEmpty(customName))
				{
					return customName;
				}
				if (string.IsNullOrEmpty(_nodeName))
				{
					NameAttribute nameAttribute = GetType().NCGetAttribute(typeof(NameAttribute), false) as NameAttribute;
					_nodeName = ((nameAttribute == null) ? GetType().Name : nameAttribute.name);
				}
				return _nodeName;
			}
			private set
			{
				customName = value;
			}
		}

		public virtual int maxInConnections
		{
			get
			{
				return -1;
			}
		}

		public virtual int maxOutConnections
		{
			get
			{
				return -1;
			}
		}

		public virtual Type outConnectionType
		{
			get
			{
				return typeof(Connection);
			}
		}

		public virtual bool allowAsPrime
		{
			get
			{
				return true;
			}
		}

		public List<Connection> inConnections
		{
			get
			{
				return _inConnections;
			}
			protected set
			{
				_inConnections = value;
			}
		}

		public List<Connection> outConnections
		{
			get
			{
				return _outConnections;
			}
			protected set
			{
				_outConnections = value;
			}
		}

		public Graph graph
		{
			get
			{
				return _graph;
			}
			private set
			{
				_graph = value;
			}
		}

		public Status status
		{
			get
			{
				return _status;
			}
			protected set
			{
				_status = value;
			}
		}

		public int ID { get; private set; }

		protected Component graphAgent
		{
			get
			{
				return (!(graph != null)) ? null : graph.agent;
			}
		}

		protected Blackboard graphBlackboard
		{
			get
			{
				return (!(graph != null)) ? null : graph.blackboard;
			}
		}

		private bool isChecked { get; set; }

		protected void Awake()
		{
			OnAwake();
		}

		protected virtual void OnAwake()
		{
		}

		public static Node Create(Graph ownerGraph, Type nodeType)
		{
			Node node = new GameObject(nodeType.Name).AddComponent(nodeType) as Node;
			node.graph = ownerGraph;
			node.transform.parent = ownerGraph.nodesRoot;
			node.transform.localPosition = Vector3.zero;
			node.UpdateNodeBBFields(ownerGraph.blackboard);
			node.OnCreate();
			return node;
		}

		protected virtual void OnCreate()
		{
		}

		public bool IsNewConnectionAllowed(Node sourceNode)
		{
			if (this == sourceNode)
			{
				Debug.LogWarning("Node can't connect to itself");
				return false;
			}
			if (sourceNode.outConnections.Count >= sourceNode.maxOutConnections && sourceNode.maxOutConnections != -1)
			{
				Debug.LogWarning("Source node can have no more out connections.");
				return false;
			}
			if (this == graph.primeNode && maxInConnections == 1)
			{
				Debug.LogWarning("Target node can have no more connections");
				return false;
			}
			if (maxInConnections <= inConnections.Count && maxInConnections != -1)
			{
				Debug.LogWarning("Target node can have no more connections");
				return false;
			}
			return true;
		}

		public Status Execute()
		{
			return Execute(graphAgent, graphBlackboard);
		}

		public Status Execute(Component agent)
		{
			return Execute(agent, graphBlackboard);
		}

		public Status Execute(Component agent, Blackboard blackboard)
		{
			if (isChecked)
			{
				return Error("Infinite Loop Detected");
			}
			isChecked = true;
			status = OnExecute(agent, blackboard);
			isChecked = false;
			return status;
		}

		protected Status Error(string log)
		{
			Debug.LogError("<b>Graph Error:</b> '" + log + "' On node '" + nodeName + "' ID " + ID + " | On graph '" + graph.graphName + "'", graph.gameObject);
			return Status.Error;
		}

		protected virtual Status OnExecute(Component agent, Blackboard blackboard)
		{
			return OnExecute(agent);
		}

		protected virtual Status OnExecute(Component agent)
		{
			return OnExecute();
		}

		protected virtual Status OnExecute()
		{
			return status;
		}

		public void ResetNode()
		{
			ResetNode(true);
		}

		public void ResetNode(bool recursively)
		{
			if (status != Status.Resting && !isChecked)
			{
				OnReset();
				status = Status.Resting;
				isChecked = true;
				for (int i = 0; i < outConnections.Count; i++)
				{
					outConnections[i].ResetConnection(recursively);
				}
				isChecked = false;
			}
		}

		protected virtual void OnReset()
		{
		}

		public void SendEvent(string name)
		{
			graph.SendEvent(name);
		}

		protected new Coroutine StartCoroutine(IEnumerator routine)
		{
			return MonoManager.current.StartCoroutine(routine);
		}

		public void UpdateNodeBBFields(Blackboard bb)
		{
			BBVariable.SetBBFields(bb, this);
		}

		public int AssignIDToGraph(Graph toNodeGraph, int lastID)
		{
			if (isChecked)
			{
				return lastID;
			}
			isChecked = true;
			lastID++;
			ID = lastID;
			for (int i = 0; i < outConnections.Count; i++)
			{
				lastID = outConnections[i].targetNode.AssignIDToGraph(toNodeGraph, lastID);
			}
			return lastID;
		}

		public void ResetRecursion()
		{
			if (isChecked)
			{
				isChecked = false;
				for (int i = 0; i < outConnections.Count; i++)
				{
					outConnections[i].targetNode.ResetRecursion();
				}
			}
		}

		public List<Node> GetParentNodes()
		{
			if (inConnections.Count != 0)
			{
				return inConnections.Select((Connection c) => c.sourceNode).ToList();
			}
			return new List<Node>();
		}

		public List<Node> GetChildNodes()
		{
			if (outConnections.Count != 0)
			{
				return outConnections.Select((Connection c) => c.targetNode).ToList();
			}
			return new List<Node>();
		}

		public virtual void OnPortConnected(int portIndex)
		{
		}

		public virtual void OnPortDisconnected(int portIndex)
		{
		}

		public virtual void OnGraphStarted()
		{
		}

		public virtual void OnGraphStoped()
		{
		}

		public virtual void OnGraphPaused()
		{
		}

		public sealed override string ToString()
		{
			ITaskAssignable taskAssignable = this as ITaskAssignable;
			return string.Format("{0} ({1})", nodeName, (taskAssignable == null || !(taskAssignable.task != null)) ? string.Empty : taskAssignable.task.ToString());
		}
	}
}
